﻿using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System;

namespace PK.Utilities.Configuration
{
    /// <summary>
    /// Base class for generic configuration element collections
    /// </summary>
    /// <typeparam name="ConfigurationElementType">A configuration element derived class</typeparam>
    public abstract class BaseConfigurationElementCollection<ConfigurationElementType> : ConfigurationElementCollection where ConfigurationElementType : ConfigurationElement, new()
    {
        #region Fields

        private ConfigurationElementCollectionType _collectionType;
        private string _elementName;

        #endregion

        #region Ctors
        /// <summary>
        /// Constructor of the class
        /// </summary>
        public BaseConfigurationElementCollection()
            : this(ConfigurationElementCollectionType.AddRemoveClearMap, null)
        { }

        /// <summary>
        /// Constructor of the class
        /// </summary>
        /// <param name="collectionType">The type of the collection</param>
        /// <param name="elementName">The name of the element in the xml</param>
        public BaseConfigurationElementCollection(ConfigurationElementCollectionType collectionType, string elementName)
        {
            this._collectionType = collectionType;
            this._elementName = elementName;
        }

        /// <summary>
        /// Constructor of the class
        /// </summary>
        /// <param name="collectionType">The type of the collection</param>
        /// <param name="elementName">The name of the element in the xml</param>
        /// <param name="comparer">The comparer object used to differentiate the elements</param>
        public BaseConfigurationElementCollection(ConfigurationElementCollectionType collectionType, string elementName, IComparer comparer)
            : base(comparer)
        {
            this._collectionType = collectionType;
            this._elementName = elementName;
        }

        #endregion

        #region Properties
        /// <summary>
        /// Gets the collection type
        /// </summary>
        public override ConfigurationElementCollectionType CollectionType
        {
            get
            {
                return this._collectionType;
            }
        }

        /// <summary>
        /// Gets the element name in the xml used to store the collection
        /// </summary>
        protected override string ElementName
        {
            get
            {
                if (string.IsNullOrEmpty(this._elementName))
                {
                    return base.ElementName;
                }
                return _elementName;
            }
        }

        /// <summary>
        /// Returns a <typeparamref name="ConfigurationElementType"/> based on the index
        /// </summary>
        /// <param name="index">A zero-based index</param>
        /// <returns>A <typeparamref name="ConfigurationElementType"/> object</returns>
        public ConfigurationElementType this[int index]
        {
            get
            {
                return (ConfigurationElementType)base.BaseGet(index);
            }
            set
            {
                if (!this.IsReadOnly() && (base.BaseGet(index) != null))
                {
                    base.BaseRemoveAt(index);
                }
                this.BaseAdd(index, value);
            }
        }

        /// <summary>
        /// Returns a <typeparamref name="ConfigurationElementType"/> object based on the element key
        /// </summary>
        /// <param name="key">The key value in the <typeparamref name="ConfigurationElementType"/> object </param>
        /// <returns>A <typeparamref name="ConfigurationElementType"/> object</returns>
        public virtual ConfigurationElementType this[object key]
        {
            get
            {
                if (key == null)
                {
                    throw new ArgumentNullException("key");
                }
                ConfigurationElementType __configurationElementType = (ConfigurationElementType)base.BaseGet(key);
                if (__configurationElementType == null)
                {
                    throw new KeyNotFoundException("ConfigKeyNotFoundInElementCollection");
                }
                return __configurationElementType;
            }
            set
            {
                if (this.IsReadOnly())
                {
                    throw new ConfigurationErrorsException("ConfigIsReadOnly");
                }
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }
                if (key == null)
                {
                    throw new ArgumentNullException("key");
                }
                if (this.GetElementKey(value).ToString().Equals((string)key, StringComparison.Ordinal))
                {
                    if (base.BaseGet(key) != null)
                    {
                        base.BaseRemove(key);
                    }
                    this.Add(value);
                }
                else
                {
                    throw new ArgumentException("ConfigKeysDoNotMatch");
                }
            }
        }

        #endregion

        #region Methods
        
        public void Add(ConfigurationElementType element)
        {
            if (this.IsReadOnly())
            {
                throw new ConfigurationErrorsException("ConfigIsReadOnly");
            }
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            base.BaseAdd(element);
        }
        public void Clear()
        {
            base.BaseClear();
        }
        public void CopyTo(ConfigurationElementType[] array, int start)
        {
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }
            if ((start < 0) || (start >= array.Length))
            {
                throw new ArgumentOutOfRangeException("start", start, string.Format("ArgumentOutOfRange_HugeArrayNotSupported:Array.Length{0}", array.Length));
            }
            this.CopyTo(array, start);
        }
        protected override ConfigurationElement CreateNewElement()
        {
            return Activator.CreateInstance<ConfigurationElementType>();
        }
        protected override object GetElementKey(ConfigurationElement element)
        {
            return GetElementKey((ConfigurationElementType)element);
        }
        protected abstract object GetElementKey(ConfigurationElementType element);
        public int IndexOf(ConfigurationElementType element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return base.BaseIndexOf(element);
        }
        public void Remove(ConfigurationElementType element)
        {
            if (this.IsReadOnly())
            {
                throw new ConfigurationErrorsException("ConfigIsReadOnly");
            }
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            base.BaseRemove(this.GetElementKey(element));
        }
        public void RemoveAt(int index)
        {
            if (this.IsReadOnly())
            {
                throw new ConfigurationErrorsException("ConfigIsReadOnly");
            }
            base.BaseRemoveAt(index);
        }
        public void RemoveAt(object key)
        {
            if (this.IsReadOnly())
            {
                throw new ConfigurationErrorsException("ConfigIsReadOnly");
            }
            if (key == null)
            {
                throw new ArgumentNullException("key");
            }
            base.BaseRemove(key);
        }

        #endregion
    }
}
